package org.jsola.contract.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.jsola.common.BeanKit;
import org.jsola.common.StrKit;
import org.jsola.contract.common.ExampleKit;
import org.jsola.contract.constant.ContractConstants;
import org.jsola.contract.dao.ISealDAO;
import org.jsola.contract.dto.IssuedSealAddDTO;
import org.jsola.contract.dto.SealAddDTO;
import org.jsola.contract.dto.SealUpdateDTO;
import org.jsola.contract.entity.CategoryDO;
import org.jsola.contract.entity.SealDO;
import org.jsola.contract.provider.ILogProviderService;
import org.jsola.contract.query.SealQuery;
import org.jsola.contract.service.ICategoryService;
import org.jsola.contract.service.ISealService;
import org.jsola.contract.vo.SealListVO;
import org.jsola.contract.vo.SealVO;
import org.jsola.core.Page;
import org.jsola.core.entity.BaseDO;
import org.jsola.exception.ParamException;
import org.jsola.log.constant.ActionType;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import tk.mybatis.mapper.entity.Example;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 印章
 *
 * @author JanusMix
 */
@Service("contractSealServiceImpl")
@Slf4j
public class SealServiceImpl implements ISealService {

    @Autowired
    private ISealDAO sealDAO;

    @Autowired
    private ICategoryService categoryService;

    /**
     * 保存印章信息
     *
     * @param sealAddDTO 印章添加DTO，包含待添加印章的信息
     * @param tokenUser 当前操作用户信息，用于记录操作者和权限校验
     * @return 保存后的印章VO对象
     *
     * 此方法主要用于添加新的印章信息到系统中它首先会根据主题ID和印章名称检查系统中是否已存在同名印章，
     * 如果存在，则抛出异常提示用户更换印章名称如果不存在，则将印章信息转换为DO对象并保存到数据库中，
     * 同时记录相关的操作日志
     */
    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public SealVO save(SealAddDTO sealAddDTO, TokenUser tokenUser) {
        // 检查是否存在同名印章
        List<SealDO> sealDOList = selectBySubjectIdsAndNames(Collections.singletonList(sealAddDTO.getSubjectId())
                , Collections.singletonList(sealAddDTO.getName()), tokenUser);
        ParamException.isTrue(CollectionUtils.isEmpty(sealDOList), "用工单位已有同名印章，请更换印章名称");

        // 转DO
        SealDO sealDO = sealAddDTO.to(SealDO.class);

        // 保存
        sealDO = save(sealDO, tokenUser.getSiteId(), tokenUser.getUserId());

        // 转VO
        SealVO sealVO = sealDO.to(SealVO.class);

        try {
            // 保存log日志
            ILogProviderService logProviderService = BeanKit.getBean(ILogProviderService.class);
            if (logProviderService == null) {
                log.error("logProviderService is null");
                return sealVO;
            }
            logProviderService.log(sealDO.getName() + "由管理员" + tokenUser.getUserName() +
                            " 新建合同印章", ActionType.SAVE.getValue(), "Seal"
                    , sealDO.getId() + "", sealDO.getSubjectId(), tokenUser);
        } catch (Exception e) {
            log.error("contract保存日志出错了", e);
        }

        return sealVO;
    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public List<SealVO> batchSave(List<SealAddDTO> sealAddDTOList, TokenUser tokenUser) {
        if (CollectionUtils.isEmpty(sealAddDTOList)) {
            return null;
        }
        List<SealDO> sealDOList = sealAddDTOList
                .parallelStream()
                .map(sealAddDTO -> {
                    SealDO sealDO = sealAddDTO.to(SealDO.class);
                    sealDO.preInsert(tokenUser.getUserId());
                    sealDO.setSiteId(tokenUser.getSiteId());
                    return sealDO;
                }).collect(Collectors.toList());
        sealDAO.insertListAndSetId(sealDOList);
        return sealDOList
                .parallelStream()
                .map(sealDO -> sealDO.to(SealVO.class))
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public int update(SealUpdateDTO sealUpdateDTO, TokenUser tokenUser) {
        // 判断是否重名
        if (StrKit.isNotEmpty(sealUpdateDTO.getName())) {
            List<SealDO> sealDOList = selectBySubjectIdsAndNames(Collections.singletonList(sealUpdateDTO.getSubjectId())
                    , Collections.singletonList(sealUpdateDTO.getName()), tokenUser);
            sealDOList.removeIf(sealDO -> sealDO.getId().equals(sealUpdateDTO.getId()));
            ParamException.isTrue(CollectionUtils.isEmpty(sealDOList), "用工单位已有同名印章，请更换印章名称");
        }
        //转DO
        SealDO sealDO = sealUpdateDTO.to(SealDO.class);
        //根据主键更新，只更新非null值
        int updateByIdSelective = updateByIdSelective(sealDO, tokenUser.getSiteId(), tokenUser.getUserId());
        SealDO newSeal = selectDOById(sealDO.getId(), tokenUser.getSiteId());
        try {
            // 保存log日志
            ILogProviderService logProviderService = BeanKit.getBean(ILogProviderService.class);
            if (logProviderService == null) {
                log.error("logProviderService is null");
                return updateByIdSelective;
            }
            logProviderService.log(newSeal.getName() + "由管理员" + tokenUser.getUserName() +
                            " 修改合同印章", ActionType.UPDATE.getValue(), "Seal"
                    , newSeal.getId() + "", newSeal.getSubjectId(), tokenUser);
        } catch (Exception e) {
            log.error("contract保存日志出错了", e);
        }
        return updateByIdSelective;
    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public int deleteByIds(TokenUser tokenUser, Long... sealIds) {
        List<SealDO> sealDOList = new ArrayList<>();
        for (Long sealId : sealIds) {
            SealDO sealDO = this.selectDOById(sealId, tokenUser.getSiteId());
            ParamException.notNull(sealDO, "印章不存在");
            sealDOList.add(sealDO);
            ParamException.isFalse(categoryService.isSealIdUsed(sealDO.getId(), tokenUser.getSiteId()),
                    "“" + sealDO.getName() + "”印章正在使用中，无法删除");
        }
        int logicalDeleteByIds = logicalDeleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), (Object[]) sealIds);
        try {
            for (SealDO sealDO : sealDOList) {
                // 保存log日志
                ILogProviderService logProviderService = BeanKit.getBean(ILogProviderService.class);
                if (logProviderService == null) {
                    log.error("logProviderService is null");
                    return logicalDeleteByIds;
                }
                logProviderService.log(sealDO.getName() + "由管理员" + tokenUser.getUserName() +
                                " 删除合同印章", ActionType.DELETE.getValue(), "Seal"
                        , sealDO.getId() + "", sealDO.getSubjectId(), tokenUser);
            }
        } catch (Exception e) {
            log.error("contract保存日志出错了", e);
        }
        return logicalDeleteByIds;
    }


    @Override
    public SealVO selectById(Long sealId, String siteId) {
        SealDO sealDO = selectDOById(sealId, siteId);
        if (sealDO == null) {
            return null;
        }
        return sealDO.to(SealVO.class);
    }

    @Override
    public List<SealDO> selectByIds(List<Long> sealIds, String siteId) {
        Example example = new Example(CategoryDO.class);
        example.and()
                .andEqualTo("siteId", siteId)
                .andIn("id", sealIds);
        return sealDAO.selectByExample(example);
    }

    @Override
    public List<SealListVO> select(SealQuery sealQuery, String siteId) {
        List<SealDO> sealDOList = selectDO(sealQuery, siteId);
        if (CollectionUtils.isEmpty(sealDOList)) {
            return sealDOList == null ? null : new ArrayList<>();
        }
        return sealDOList.stream()
                .map(sealDO -> sealDO.to(SealListVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public List<SealDO> selectBySubjectId(String subjectId, String siteId) {
        if (StrKit.isEmpty(subjectId)) {
            return new ArrayList<>();
        }
        Example example = new Example(SealDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId)
                .andEqualTo("subjectId", subjectId);
        return sealDAO.selectByExample(example);
    }

    @Override
    public int selectCount(SealQuery sealQuery, String siteId) {
        Example example = buildExample(sealQuery, siteId);
        return sealDAO.selectCountByExample(example);
    }

    @Override
    public Page<SealListVO> selectPage(SealQuery sealQuery, String siteId) {
        Example example = buildExample(sealQuery, siteId);
        Page<SealDO> page = sealDAO.selectPageByExample(example,
                sealQuery.getPageNo(),
                sealQuery.getPageSize());

        return page.to(SealListVO.class);
    }

    @Override
    public SealDO selectDOById(Long sealId, String siteId) {
        return listById(sealId, siteId);
    }

    @Override
    public List<SealDO> selectDO(SealQuery sealQuery, String siteId) {
        Example example = buildExample(sealQuery, siteId);
        return sealDAO.selectByExample(example);
    }

    @Override
    public List<SealVO> saveIssuedSeal(IssuedSealAddDTO issuedSealAddDTO, TokenUser tokenUser) {
        List<SealVO> sealVOList = new ArrayList<>();
        List<SealAddDTO> sealAddDTOList = new ArrayList<>();
        List<SealDO> sealDOList = selectByIds(issuedSealAddDTO.getCategoryIds(), tokenUser.getSiteId());
        String names = sealDOList.parallelStream().filter(sealDO -> sealDO.getStatus() == null
                        || sealDO.getStatus() != 1).map(SealDO::getName).distinct()
                .collect(Collectors.joining(","));
        if (!StringUtils.isEmpty(names)) {
            throw new ParamException("印章：[" + names + "]未认证，无法下发");
        }
        List<String> sealNames = sealDOList.parallelStream().map(SealDO::getName).collect(Collectors.toList());
        // 用工单位原有同名印章
        List<SealDO> oldSealList = selectBySubjectIdsAndNames(issuedSealAddDTO.getSubjectIds(), sealNames, tokenUser);
        // 按用工单位分组
        Map<String, List<SealDO>> subjectIdListMap = oldSealList.parallelStream()
                .collect(Collectors.groupingBy(SealDO::getSubjectId));
        // 下发前 用工单位Id去重
        for (String subjectId : issuedSealAddDTO.getSubjectIds().parallelStream().distinct().collect(Collectors.toList())) {
            List<SealDO> hadSealDOList = subjectIdListMap.get(subjectId);
            Map<String, Long> sealNameId = new HashMap<>();
            if (!CollectionUtils.isEmpty(hadSealDOList)) {
                sealNameId = hadSealDOList.parallelStream()
                        .collect(Collectors.toMap(SealDO::getName, BaseDO::getId, (v1, v2) -> v1));
            }
            // 需要下发的印章
            for (SealDO sealDO : sealDOList) {
                Long sealId = sealNameId.get(sealDO.getName());
                if (sealId == null) {
                    // 用工单位下无同名印章 新增
                    SealAddDTO sealAddDTO = sealDO.to(SealAddDTO.class);
                    sealAddDTO.setSubjectId(subjectId);
                    sealAddDTO.setIssue(true);
                    sealAddDTO.setSourceId(sealDO.getId() + "");
                    sealAddDTO.setOrganizationId(sealDO.getOrganizationId());
                    sealAddDTO.setThirdSealId(sealDO.getThirdSealId());
                    sealAddDTOList.add(sealAddDTO);

                } else {
                    // 用工单位下有同名印章 更新
                    SealUpdateDTO sealUpdateDTO = sealDO.to(SealUpdateDTO.class);
                    sealUpdateDTO.setId(sealId);
                    sealUpdateDTO.setSubjectId(subjectId);
                    sealUpdateDTO.setIssue(true);
                    sealUpdateDTO.setSourceId(sealDO.getId() + "");
                    sealUpdateDTO.setOrganizationId(sealDO.getOrganizationId());
                    sealUpdateDTO.setThirdSealId(sealDO.getThirdSealId());
                    update(sealUpdateDTO, tokenUser);
                    sealVOList.add(sealUpdateDTO.to(SealVO.class));
                }
            }
        }
        List<SealVO> batchSave = batchSave(sealAddDTOList, tokenUser);
        if (!CollectionUtils.isEmpty(batchSave)) {
            sealVOList.addAll(batchSave);
        }
        return sealVOList;
    }

    /**
     * 根据主体ID和印章名称筛选印章记录
     *
     * 此方法用于从数据库中选择符合条件的印章记录（SealDO）它主要基于两个筛选条件：
     * 1. 主体ID（subjectIds）：一个包含多个主体ID的列表，用于指定需要查询的公司或实体
     * 2. 印章名称（sealNames）：一个包含多个印章名称的列表，用于指定需要查询的印章类型
     *
     * 此外，方法还会验证输入参数是否为空，以确保查询的有效性，并基于传入的用户令牌（tokenUser）
     * 限制查询结果只属于特定的站点（siteId）
     *
     * @param subjectIds 主体ID列表，用于指定查询的公司或实体范围，不能为空
     * @param sealNames 印章名称列表，用于指定查询的印章类型范围，不能为空
     * @param tokenUser 当前请求的用户信息，包含站点ID等用于权限控制的数据
     * @return 返回一个印章记录列表（SealDO），这些记录满足给定的主体ID和印章名称条件
     */
    @Override
    public List<SealDO> selectBySubjectIdsAndNames(List<String> subjectIds, List<String> sealNames, TokenUser tokenUser) {
        // 验证主体ID列表不为空，因为没有主体ID无法进行有意义的查询
        ParamException.notEmpty(subjectIds, "未选择公司");
        // 验证印章名称列表不为空，因为没有印章名称无法进行有意义的查询
        ParamException.notEmpty(sealNames, "没有需要下发的印章");

        // 创建一个查询示例（Example）对象，用于构建复杂的查询条件
        Example example = new Example(SealDO.class);
        example.and()
                // 添加条件：印章记录必须是有效的（valid=true）
                .andEqualTo("valid", true)
                // 添加条件：印章记录必须属于用户所在的站点
                .andEqualTo("siteId", tokenUser.getSiteId())
                // 添加条件：印章记录的主体ID必须在给定的主体ID列表中
                .andIn("subjectId", subjectIds)
                // 添加条件：印章记录的名称必须在给定的印章名称列表中
                .andIn("name", sealNames);

        // 执行查询并返回结果
        return sealDAO.selectByExample(example);
    }

    /**
     * 根据查询参数，构建example
     *
     * @param sealQuery 查询参数
     * @param siteId    所属站点id
     * @return example
     */
    private Example buildExample(SealQuery sealQuery, String siteId) {
        Example example = new Example(SealDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        if (sealQuery.getSubjectId() != null) {
            example.and().andEqualTo("subjectId", sealQuery.getSubjectId());
        }
        if (sealQuery.getName() != null) {
            example.and().andLike("name", "%" + sealQuery.getName() + "%");
        }
        if (sealQuery.getSourceId() != null) {
            example.and().andEqualTo("sourceId", sealQuery.getSourceId());
        }
        if (sealQuery.getDescription() != null) {
            example.and().andEqualTo("description", sealQuery.getDescription());
        }
        if (sealQuery.getSealString() != null) {
            example.and().andEqualTo("sealString", sealQuery.getSealString());
        }
        if (sealQuery.getContent() != null) {
            example.and().andEqualTo("content", sealQuery.getContent());
        }
        if (sealQuery.getColor() != null) {
            example.and().andEqualTo("color", sealQuery.getColor());
        }
        if (sealQuery.getFont() != null) {
            example.and().andEqualTo("font", sealQuery.getFont());
        }
        if (sealQuery.getFontSize() != null) {
            example.and().andEqualTo("fontSize", sealQuery.getFontSize());
        }
        if (sealQuery.getPersonalSealType() != null) {
            example.and().andEqualTo("personalSealType", sealQuery.getPersonalSealType());
        }
        if (sealQuery.getEnterpriseSealType() != null) {
            example.and().andEqualTo("enterpriseSealType", sealQuery.getEnterpriseSealType());
        }
        if (sealQuery.getText() != null) {
            example.and().andEqualTo("text", sealQuery.getText());
        }
        if (sealQuery.getRoundText() != null) {
            example.and().andEqualTo("roundText", sealQuery.getRoundText());
        }
        if (sealQuery.getRowText() != null) {
            example.and().andEqualTo("rowText", sealQuery.getRowText());
        }
        if (sealQuery.getBottomText() != null) {
            example.and().andEqualTo("bottomText", sealQuery.getBottomText());
        }
        if (sealQuery.getStatus() != null) {
            example.and().andEqualTo("status", sealQuery.getStatus());
        }
        // 排序
        ExampleKit.setExampleOrder(example, sealQuery.getOrders());
        return example;
    }
}




